package com.senmall.seckill.service.impl;

import com.senmall.auth.entity.UserInfo;
import com.senmall.item.pojo.SeckillGoods;
import com.senmall.item.pojo.Stock;
import com.senmall.order.pojo.Order;
import com.senmall.order.pojo.OrderDetail;
import com.senmall.order.pojo.SeckillOrder;
import com.senmall.seckill.mapper.SeckillOrderMapper;
import com.senmall.seckill.mapper.SkuMapper;
import com.senmall.seckill.vo.SeckillMessage;
import com.senmall.seckill.client.GoodsClient;
import com.senmall.seckill.client.OrderClient;
import com.senmall.seckill.mapper.SeckillMapper;
import com.senmall.seckill.mapper.StockMapper;
import com.senmall.seckill.service.SeckillService;
import com.senmall.common.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @Author: senrian
 * @Time: 2019-11-10 16:57
 * @Feature:
 */
@Service
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private SeckillMapper seckillMapper;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private OrderClient orderClient;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private SeckillOrderMapper seckillOrderMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    private static final Logger LOGGER = LoggerFactory.getLogger(SeckillServiceImpl.class);

    private static final String KEY_PREFIX_PATH = "senmall:seckill:path";
    private static final String KEY_SECKILL_VERTIFYCODE = "senmall:seckill:vertifyCode";



    /**
     * 创建订单
     * @param seckillGoods
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createOrder(SeckillGoods seckillGoods) {

        Order order = new Order();
        order.setPaymentType(1);
        order.setTotalPay(seckillGoods.getSeckillPrice());
        order.setActualPay(seckillGoods.getSeckillPrice());
        order.setPostFee(0+"");
        order.setReceiver("李四");
        order.setReceiverMobile("15812312312");
        order.setReceiverCity("西安");
        order.setReceiverDistrict("碑林区");
        order.setReceiverState("陕西");
        order.setReceiverZip("000000000");
        order.setInvoiceType(0);
        order.setSourceType(2);

        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setSkuId(seckillGoods.getSkuId());
        orderDetail.setNum(1);
        orderDetail.setTitle(seckillGoods.getTitle());
        orderDetail.setImage(seckillGoods.getImage());
        orderDetail.setPrice(seckillGoods.getSeckillPrice());
        orderDetail.setOwnSpec(this.skuMapper.selectByPrimaryKey(seckillGoods.getSkuId()).getOwnSpec());

        order.setOrderDetails(Arrays.asList(orderDetail));


        String seck = "seckill";
        ResponseEntity<List<Long>> responseEntity = this.orderClient.createOrder(seck,order);

        if (responseEntity.getStatusCode() == HttpStatus.OK){
            //库存不足，返回null
            return null;
        }
        return responseEntity.getBody().get(0);
    }

    /**
     * 检查秒杀库存
     * @param skuId
     * @return
     */
    @Override
    public boolean queryStock(Long skuId) {
        Stock stock = this.stockMapper.selectByPrimaryKey(skuId);
        if (stock.getSeckillStock() - 1 < 0){
            return false;
        }
        return true;
    }

    /**
     * 发送消息到秒杀队列当中
     * @param seckillMessage
     */
    @Override
    public void sendMessage(SeckillMessage seckillMessage) {
        String json = JsonUtils.serialize(seckillMessage);
        try {
            this.amqpTemplate.convertAndSend("order.seckill", json);
        }catch (Exception e){
            LOGGER.error("秒杀商品消息发送异常，商品id：{}",seckillMessage.getSeckillGoods().getSkuId(),e);
        }
    }

    /**
     * 根据用户id查询秒杀订单
     * @param userId
     * @return
     */
    @Override
    public Long checkSeckillOrder(Long userId) {
        Example example = new Example(SeckillOrder.class);
        example.createCriteria().andEqualTo("userId",userId);
        List<SeckillOrder> seckillOrders = this.seckillOrderMapper.selectByExample(example);
        if (seckillOrders == null || seckillOrders.size() == 0){
            return null;
        }
        return seckillOrders.get(0).getOrderId();
    }

    /**
     * 创建秒杀地址
     * @param goodsId
     * @param id
     * @return
     */
    @Override
    public String createPath(Long goodsId, Long id) {
        String str = new BCryptPasswordEncoder().encode(goodsId.toString()+id);
        BoundHashOperations<String,Object,Object> hashOperations = this.stringRedisTemplate.boundHashOps(KEY_PREFIX_PATH);
        String key = id.toString() + "_" + goodsId;
        hashOperations.put(key,str);
        //60秒过期
        hashOperations.expire(60, TimeUnit.SECONDS);
        return str;
    }

    /**
     * 验证秒杀地址
     * @param goodsId
     * @param id
     * @param path
     * @return
     */
    @Override
    public boolean checkSeckillPath(Long goodsId, Long id, String path) {
        String key = id.toString() + "_" + goodsId;
        BoundHashOperations<String,Object,Object> hashOperations = this.stringRedisTemplate.boundHashOps(KEY_PREFIX_PATH);
        String encodePath = (String) hashOperations.get(key);
        return new BCryptPasswordEncoder().matches(path,encodePath);
    }

    /**
     * 查询秒杀商品
     * @return
     */
    @Override
    public List<SeckillGoods> querySeckillGoods() {
        Example example = new Example(SeckillGoods.class);
        example.createCriteria().andEqualTo("enable",true);
        return this.seckillMapper.selectByExample(example);
    }

    @Override
    public BufferedImage createMiaoshaVertifyCode(UserInfo user, Long goodsId) {
        if(user==null||goodsId<=0) {
            return null;
        }
        int width=80;
        int height=30;
        BufferedImage img=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        Graphics g=img.getGraphics();
        g.setColor(new Color(0xDCDCDC));
        g.fillRect(0, 0, width, height);
        g.setColor(Color.BLACK);
        g.drawRect(0, 0, width-1, height-1);
        Random rdm=new Random();
        for(int i=0;i<50;i++) {
            int x=rdm.nextInt(width);
            int y=rdm.nextInt(height);
            g.drawOval(x, y, 0, 0);
        }
        //生成验证码
        String vertifyCode=createVertifyCode(rdm);
        g.setColor(new Color(0,100,0));
        g.setFont(new Font("Candara",Font.BOLD,24));
        //将验证码写在图片上
        g.drawString(vertifyCode, 8, 24);
        g.dispose();
        //计算存值
        int rnd=calc(vertifyCode);
        //将计算结果保存到redis上面去
        BoundHashOperations<String,Object,Object> hashOperations = this.stringRedisTemplate.boundHashOps(KEY_SECKILL_VERTIFYCODE);
        hashOperations.put(""+user.getId()+"_"+goodsId, rnd);
        return img;
    }

    @Override
    public boolean checkVCode(UserInfo user, Long goodsId, int vertifyCode) {
        BoundHashOperations<String,Object,Object> hashOperations = this.stringRedisTemplate.boundHashOps(KEY_SECKILL_VERTIFYCODE);

        Integer redisVCode= (Integer) hashOperations.get(user.getId()+"_"+goodsId);
        if(redisVCode==null||redisVCode-vertifyCode!=0) {
            return false;
        }
        //删除缓存里面的数据
        hashOperations.delete(user.getId()+"_"+goodsId);
        return true;
    }


    private static int calc(String exp) {
        try {
            ScriptEngineManager manager=new ScriptEngineManager();
            ScriptEngine engine=manager.getEngineByName("JavaScript");
            return (Integer) engine.eval(exp);
        }catch(Exception e){
            e.printStackTrace();
            return 0;
        }
    }

    private static char[]ops=new char[] {'+','-','*'};
    private String createVertifyCode(Random rdm) {
        //生成10以内的
        int n1=rdm.nextInt(10);
        int n2=rdm.nextInt(10);
        int n3=rdm.nextInt(10);
        char op1=ops[rdm.nextInt(3)];//0  1  2
        char op2=ops[rdm.nextInt(3)];//0  1  2
        String exp=""+n1+op1+n2+op2+n3;
        return exp;
    }
}
